/*
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*         http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

#include <gtest/gtest.h>
#include <test_utilities/test_base.h>
#include "cx5_port.h"
#include "cx5_card.h"
#include <mc/engine.h>
#include <mc/common.h>
#include <mc/variant.h>
#include <ctime>
#include <memory>
#include <vector>

namespace drivers {
namespace pcie_nic_card {
namespace mellanox {
namespace cx5 {
namespace test {

// Mock IPMI Core 接口，避免真实的 D-Bus 调用
class test_ipmi_core_interface : public mc::engine::interface<test_ipmi_core_interface> {
public:
    MC_INTERFACE("bmc.kepler.IpmiCore")

    // a{ss}yyyyyay
    mc::variant Request(
        const mc::dict& ctx,              // a{ss} - 上下文字典
        uint8_t channel_type,              // y - 通道类型
        uint8_t instance,                  // y - 实例序号
        uint8_t net_fn,                   // y - 网络功能码
        uint8_t lun,                      // y - 逻辑单元号
        uint8_t cmd,                      // y - 命令字
        const std::vector<uint8_t>& data  // ay - 命令参数数据
    ) {
        ilog("test_ipmi_core_interface: Request - net_fn=${net_fn}, cmd=${cmd}", ("net_fn", net_fn)("cmd", cmd));
        // 返回模拟的成功响应，避免真实的 D-Bus 调用
        mc::variants result = {mc::variant(static_cast<int64_t>(0)), mc::variant(std::vector<uint8_t>{0x00, 0x01, 0x02})};
        return mc::variant(result);
    }
};

class test_ipmi_core_object : public mc::engine::object<test_ipmi_core_object> {
public:
    MC_OBJECT(test_ipmi_core_object, "bmc.kepler.IpmiCore", "/bmc/kepler/IpmiCore", (test_ipmi_core_interface))

    test_ipmi_core_interface m_test_interface;
};

class cx5_test_service : public mc::engine::service {
public:
    cx5_test_service(const std::string& name) : mc::engine::service(name) {}
};

// 辅助函数：清理 cx5_card 的成员变量，避免析构时访问未初始化的指针
static void safe_cleanup_cx5_card(dev::cx5_card* card) {
    if (card == nullptr) {
        return;
    }
    try {
        card->m_network_adapter.stop_ncsi_update_task();
    } catch (...) {
    }
    try {
        card->m_network_adapter_cooling.stop_ncsi_update_task();
    } catch (...) {
    }
    try {
        card->m_pcie_device.stop_ncsi_update_task();
    } catch (...) {
    }
    try {
        card->m_network_adapter.unregister_os_reset_callback();
    } catch (...) {
    }
}

// 自定义删除器：在删除前先清理资源
struct cx5_card_deleter {
    void operator()(dev::cx5_card* card) const {
        safe_cleanup_cx5_card(card);
        delete card;
    }
};

// 辅助函数：清理 cx5_port 的成员变量
static void safe_cleanup_cx5_port(dev::cx5_port* port) {
    if (port == nullptr) {
        return;
    }
    try {
        port->m_network_port.stop_ncsi_update_task();
    } catch (...) {
    }
    try {
        port->m_network_port_link_info.stop_ncsi_update_task();
    } catch (...) {
    }
    try {
        port->m_network_port_metrics.stop_ncsi_update_task();
    } catch (...) {
    }
}

// 自定义删除器：在删除前先清理资源
struct cx5_port_deleter {
    void operator()(dev::cx5_port* port) const {
        safe_cleanup_cx5_port(port);
        delete port;
    }
};

// ========================= cx5_port测试 =========================
class Cx5PortTest : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        // 使用与 IPMB 库期望的服务名一致的服务名，避免超时
        // IPMB 库查找的服务名是 "bmc.kepler.ipmi_core"（定义在 libraries/ipmb/ipmb.h）
        m_test_service = std::make_unique<cx5_test_service>("bmc.kepler.ipmi_core");
        m_test_service->init();
        m_test_service->start();
        
        // 注册 mock IpmiCore 接口，避免真实的 D-Bus 调用
        m_ipmi_core_obj = test_ipmi_core_object::create();
        m_ipmi_core_obj->set_object_path("/bmc/kepler/IpmiCore");
        m_ipmi_core_obj->set_object_name("bmc.kepler.IpmiCore");
        m_test_service->register_object(m_ipmi_core_obj);
    }
    static void TearDownTestSuite() {
        try {
            if (m_ipmi_core_obj && m_test_service) {
                m_test_service->unregister_object(m_ipmi_core_obj);
                m_ipmi_core_obj.reset();
            }
            if (m_test_service) {
                m_test_service->stop();
                m_test_service.reset();
            }
            TestWithEngine::TearDownTestSuite();
        } catch (...) {
            if (m_ipmi_core_obj) {
                m_ipmi_core_obj.reset();
            }
            if (m_test_service) {
                m_test_service.reset();
            }
        }
    }
    void SetUp() override {
        static int test_counter = 0;
        test_counter++;
        std::string card_name = "test_cx5_card_" + std::to_string(test_counter) + "_" + std::to_string(std::time(nullptr));
        std::string port_name = "test_cx5_port_" + std::to_string(test_counter) + "_" + std::to_string(std::time(nullptr));
        std::string position = std::to_string(test_counter + 200);

        m_card = mc::make_shared<dev::cx5_card>();
        m_card->set_service(m_test_service.get());
        m_card->set_object_name(card_name.c_str());
        m_card->set_position(position.c_str());
        try {
            m_test_service->register_object(m_card);
        } catch (...) {
            // 忽略注册时的异常，可能在某些测试中不需要注册
        }

        m_port = mc::make_shared<dev::cx5_port>();
        m_port->set_service(m_test_service.get());
        m_port->set_object_name(port_name.c_str());
        m_port->set_position((position + "_port").c_str());
        m_port->set_parent(m_card.get());
        try {
            m_test_service->register_object(m_port);
        } catch (...) {
            // 忽略注册时的异常，可能在某些测试中不需要注册
        }
    }
    void TearDown() override {
        try {
            if (m_port && m_test_service) {
                try {
                    m_port->stop();
                } catch (...) {
                    // 忽略 stop 时的异常（对象可能未初始化）
                }
                try {
                    m_test_service->unregister_object(m_port);
                } catch (...) {
                    // 忽略注销时的异常
                }
                m_port.reset();
            }
            if (m_card && m_test_service) {
                try {
                    m_card->stop();
                } catch (...) {
                    // 忽略 stop 时的异常（对象可能未初始化）
                }
                try {
                    m_test_service->unregister_object(m_card);
                } catch (...) {
                    // 忽略注销时的异常
                }
                m_card.reset();
            }
        } catch (...) {
            if (m_port && m_test_service) {
                try {
                    m_test_service->unregister_object(m_port);
                } catch (...) {
                }
                m_port.reset();
            }
            if (m_card && m_test_service) {
                try {
                    m_test_service->unregister_object(m_card);
                } catch (...) {
                }
                m_card.reset();
            }
        }
    }

    mc::mutable_dict create_test_csr() {
        return mc::mutable_dict{
            {"PortId", 0},
        };
    }

    mc::dict create_test_connector() {
        return mc::dict{{"SystemId", 1}};
    }

    static std::unique_ptr<cx5_test_service> m_test_service;
    static mc::shared_ptr<test_ipmi_core_object> m_ipmi_core_obj;
    mc::shared_ptr<dev::cx5_port> m_port;
    mc::shared_ptr<dev::cx5_card> m_card;
};
std::unique_ptr<cx5_test_service> Cx5PortTest::m_test_service;
mc::shared_ptr<test_ipmi_core_object> Cx5PortTest::m_ipmi_core_obj;

} // namespace test
} // namespace cx5
} // namespace mellanox
} // namespace pcie_nic_card
} // namespace drivers

// 添加反射元数据定义（必须在命名空间外）
MC_REFLECT(drivers::pcie_nic_card::mellanox::cx5::test::test_ipmi_core_interface, (Request))
MC_REFLECT(drivers::pcie_nic_card::mellanox::cx5::test::test_ipmi_core_object, ((m_test_interface, "bmc.kepler.IpmiCore")))

using namespace drivers::pcie_nic_card::mellanox::cx5::test;

// ========================= 基本功能测试 =========================

TEST_F(Cx5PortTest, Constructor) {
    // Constructor 测试只验证对象能否正确创建
    // 直接创建栈对象，无需复杂配置，由 RAII 自动管理生命周期
    dev::cx5_port port;
    EXPECT_TRUE(true);
}

TEST_F(Cx5PortTest, StartStop_Basic) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    EXPECT_TRUE(m_port->start());
    EXPECT_TRUE(m_port->stop());
}

TEST_F(Cx5PortTest, Init_Success) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    bool result = m_port->init(csr, connector);
    EXPECT_TRUE(result);
}

TEST_F(Cx5PortTest, Init_Exception) {
    mc::mutable_dict empty_csr;
    mc::dict empty_connector;
    bool result = m_port->init(empty_csr, empty_connector);
    EXPECT_TRUE(result == true || result == false);
}

// ========================= 多次操作测试 =========================

TEST_F(Cx5PortTest, MultipleStartStop) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    EXPECT_TRUE(m_port->start());
    // 由于使用了 mock 接口，无需等待真实的 D-Bus 调用完成
    EXPECT_TRUE(m_port->stop());
}

// ========================= 异常处理测试 =========================

TEST_F(Cx5PortTest, Start_WithoutInit) {
    bool result = m_port->start();
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, Stop_WithoutStart) {
    bool result = m_port->stop();
    EXPECT_TRUE(result);
}

TEST_F(Cx5PortTest, UpdatePortBdf_Success) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 1;
    pcie_device_info.device = 0;
    pcie_device_info.function = 0;
    pcie_device_info.socket_id = 0;

    bool result = m_port->update_port_bdf(pcie_device_info);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, UpdatePortBdf_ImuNotInitialized) {
    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 1;
    pcie_device_info.device = 0;
    pcie_device_info.function = 0;
    pcie_device_info.socket_id = 0;

    bool result = m_port->update_port_bdf(pcie_device_info);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, VerifyDidVidSvidSdid_ImuNotInitialized) {
    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 1;
    pcie_device_info.device = 0;
    pcie_device_info.function = 0;
    pcie_device_info.socket_id = 0;

    bool result = m_port->verify_did_vid_svid_sdid(pcie_device_info);
    EXPECT_FALSE(result);
}

TEST_F(Cx5PortTest, UpdatePortBdf_VerifyFailed) {
    auto csr = create_test_csr();
    csr["PortId"] = 5;
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 1;
    pcie_device_info.device = 0;
    pcie_device_info.function = 0;
    pcie_device_info.socket_id = 0;

    bool result = m_port->update_port_bdf(pcie_device_info);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, UpdatePortBdf_WithPortId) {
    auto csr = create_test_csr();
    csr["PortId"] = 3;
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 0x10;
    pcie_device_info.device = 0x02;
    pcie_device_info.function = 0x03;
    pcie_device_info.socket_id = 0;

    uint8_t original_bus = pcie_device_info.bus;
    bool result = m_port->update_port_bdf(pcie_device_info);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, VerifyDidVidSvidSdid_WithImuInitialized) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 0x18;
    pcie_device_info.device = 0x02;
    pcie_device_info.function = 0x03;
    pcie_device_info.socket_id = 0;

    bool result = m_port->verify_did_vid_svid_sdid(pcie_device_info);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, Init_WithPortId) {
    auto csr = create_test_csr();
    csr["PortId"] = 5;
    auto connector = create_test_connector();
    bool result = m_port->init(csr, connector);
    EXPECT_TRUE(result);
}

TEST_F(Cx5PortTest, Init_WithInvalidData) {
    mc::mutable_dict csr;
    csr["PortId"] = "invalid";
    mc::dict connector;
    bool result = m_port->init(csr, connector);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, UpdatePortBdf_BdfFormatting) {
    auto csr = create_test_csr();
    csr["PortId"] = 0;
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 0x18;
    pcie_device_info.device = 0x02;
    pcie_device_info.function = 0x03;
    pcie_device_info.socket_id = 0;

    bool result = m_port->update_port_bdf(pcie_device_info);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, UpdatePortBdf_WithNonZeroPortId) {
    auto csr = create_test_csr();
    csr["PortId"] = 2;
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    dev::imu_pcie_device_req_t pcie_device_info = {};
    pcie_device_info.bus = 0x10;
    pcie_device_info.device = 0x01;
    pcie_device_info.function = 0x00;
    pcie_device_info.socket_id = 0;

    uint8_t original_bus = pcie_device_info.bus;
    bool result = m_port->update_port_bdf(pcie_device_info);
    EXPECT_TRUE(result == true || result == false);
}

// ========================= NCSI协议测试 =========================

TEST_F(Cx5PortTest, StartNcsiProtocol_WithNullNcsi) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    dev::ncsi_over_mctp_mellanox_ptr ncsi_over_mctp = nullptr;
    mc::milliseconds interval(5000);
    uint8_t package_id = 0;

    bool result = m_port->start_ncsi_protocol(ncsi_over_mctp, interval, package_id);
    EXPECT_TRUE(result == true || result == false);
}

TEST_F(Cx5PortTest, StopNcsiProtocol_WithoutStart) {
    m_port->stop_ncsi_protocol();
    EXPECT_TRUE(true);
}

TEST_F(Cx5PortTest, StopNcsiProtocol_AfterStart) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();
    m_port->init(csr, connector);

    m_port->stop_ncsi_protocol();
    EXPECT_TRUE(true);
}

// ========================= 多实例测试 =========================

TEST_F(Cx5PortTest, MultiInstance) {
    auto card = mc::make_shared<dev::cx5_card>();
    card->set_service(m_test_service.get());
    card->set_object_name("multi_card");
    card->set_position("10");
    m_test_service->register_object(card);

    std::vector<mc::shared_ptr<dev::cx5_port>> ports;
    for (int i = 0; i < 3; i++) {
        auto port = mc::make_shared<dev::cx5_port>();
        port->set_service(m_test_service.get());
        port->set_object_name(("port_" + std::to_string(i+1)).c_str());
        port->set_position(std::to_string(i + 10).c_str());
        port->set_parent(card.get());
        auto csr = create_test_csr();
        csr["PortId"] = i;
        auto connector = create_test_connector();
        port->init(csr, connector);
        m_test_service->register_object(port);

        EXPECT_TRUE(port->start());
        ports.push_back(port);
    }

    for (auto& port : ports) {
        if (port && m_test_service) {
            port->stop();
            m_test_service->unregister_object(port);
        }
    }
    if (card && m_test_service) {
        card->stop();
        m_test_service->unregister_object(card);
    }
}

// ========================= 多次调用测试 =========================

TEST_F(Cx5PortTest, MultipleInit) {
    auto csr = create_test_csr();
    auto connector = create_test_connector();

    EXPECT_TRUE(m_port->init(csr, connector));
    EXPECT_TRUE(m_port->init(csr, connector));
}
